package com.xtm.exercise.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSAUtils {

    private static final String ALGORITHM = "RSA";
    private static final String PUBLICK_EY = "PUBLICK_EY";
    private static final String PRIVATE_KEY = "PRIVATE_KEY";
//    public static final String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOllW46eZ0nwG1HYyScVTiTcsWieMme5vw3W/MRGbmXicVPZ1FxAtCgzo2jASAADcy5Nk0n2hwiGAEY2aagcKZxNNR7gI+jhIBT4TilXLOpAR4jJ18iEXFvafu6KyB2xqqVDMrMzs27ABYIJFf6PPYEgGCFoYnGALouB1hM7eKvxAgMBAAECgYEAzcyuGufaM/wgWt9JH8lNint/g7xavdOvf8ieQYPJPNWbEFD3TBFRylEeOfOyiTCBk8abpZBYKU/fVgoMyEts89KNFAk+PnCNLbCs0M5idwWfxu7uLY/fb0FUR/EVRcA9M5ZL8YP27jwPcQgP6Y5RsAS7ALNsKUjhcRAj5bBoyHECQQD4nzH7IzHoztyHCNJTR/dOKeD/qfH/wKHcWCMliHio9YmavPvomTWTHb9n8sKpHF1ztl+zCF88WvE8qiYNfHEHAkEA8FJ9UcsK3I1DcDbbLJCYiclAvH6M1FNu21oEaYgNsvKUTDG1KtmkYrIU7E0rf4RBHPqMpOdnCFQibFibqqJVRwJAUIfgPKSNygcXT3YACpHI/Tx7cNSvWlQfGaxC1il6S+Acjk6gmhrmS5zbHuR3QSe67XwpS2hu0LH0ad4DLWMdiQJAaBkTN67f0ZFPOPhKEA/CQ0ztwbh/gClzx4hCiQPqCy2Ql0Cg3SLRVf8VmRYEVYQQQGE3MucZdj4bexsWst8GWQJBAIoh+lFUzFATcd21ATfFtqK2kumVrqeauESokEwWjtvWdbZX1hmHBcNTEniRUyecQlzT7UnBzbMLONpV3tis1Jg=";
//    public static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpZVuOnmdJ8BtR2MknFU4k3LFonjJnub8N1vzERm5l4nFT2dRcQLQoM6NowEgAA3MuTZNJ9ocIhgBGNmmoHCmcTTUe4CPo4SAU+E4pVyzqQEeIydfIhFxb2n7uisgdsaqlQzKzM7NuwAWCCRX+jz2BIBghaGJxgC6LgdYTO3ir8QIDAQAB";


    public static final String publicKey =   "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH" +
            "nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==";
    public static final String privateKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY" +
            "7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN" +
            "PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA" +
            "kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow" +
            "cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv" +
            "DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh" +
            "YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3" +
            "UP8iWi1Qw0Y=";
    /**
     * 加密算法
     */
    private static final String CIPHER_DE = "RSA";
    /**
     * 解密算法
     */
    private static final String CIPHER_EN = "RSA";
    /**
     * 密钥长度
     */
    private static final Integer KEY_LENGTH = 1024;

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 生成秘钥对，公钥和私钥
     *
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static Map<String, Object> genKeyPair() throws NoSuchAlgorithmException {
        Map<String, Object> keyMap = new HashMap<String, Object>();
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_LENGTH); // 秘钥字节数
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        keyMap.put(PUBLICK_EY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    /**
     * 公钥加密
     *
     * @param data
     * @param publicKey
     * @return
     * @throws InvalidKeySpecException
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        // 得到公钥
        byte[] keyBytes = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        Key key = keyFactory.generatePublic(x509EncodedKeySpec);
        // 加密数据，分段加密
        Cipher cipher = Cipher.getInstance(CIPHER_EN);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        int inputLength = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        while (inputLength - offset > 0) {
            if (inputLength - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offset, inputLength - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * 私钥解密
     *
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        // 得到私钥
        byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        Key key = keyFactory.generatePrivate(pKCS8EncodedKeySpec);
        // 解密数据，分段解密
        Cipher cipher = Cipher.getInstance(CIPHER_DE);
        cipher.init(Cipher.DECRYPT_MODE, key);
        int inputLength = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        byte[] tmp;
        while (inputLength - offset > 0) {
            if (inputLength - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offset, inputLength - offset);
            }
//            out.write(cache, 0, cache.length);
            out.write(cache);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * 获取公钥
     *
     * @param keyMap
     * @return
     */
    public static String getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLICK_EY);
        String str = new String(Base64.encodeBase64(key.getEncoded()));
        return str;
    }

    /**
     * 获取私钥
     *
     * @param keyMap
     * @return
     */
    public static String getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        String str = new String(Base64.encodeBase64(key.getEncoded()));
        return str;
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> keyMap = RSAUtils.genKeyPair();
        String publicKey = RSAUtils.getPublicKey(keyMap);
        String privateKey = RSAUtils.getPrivateKey(keyMap);
        System.out.println("公钥：" + publicKey);
        System.out.println("私钥：" + privateKey);
        // 公钥加密
//        String sourceStr = "111111"; // Q+DEC7/AFxIzk0obzXVyZYZsw9UAit4roDdhijAa8dXmySzQxzAgmxbnPAs+tsR5AhdqO9L7wrb2jIyiBXGcDtpNWKnHzecF04RKhHTJUKqFV00L/WBhzEJ9to31LzQ/Yqggtkw/H1Yq10aE2WRDVMYIadFmeGp655zRdVFRdgI=
        String sourceStr = "{\"username\":\"admin\",\"password\":\"111111\"}"; // IxYJwkM8AScjiHyszI4cuoD4bEvj4eXrZ6LPUgom0zm8rly9jFTpQRcmNm+2M9PRxz12AM17HJWzBYv+rTdUc73XcpCEfOOV5mlxUd5Hy49Bdj92iM/FhzY38OzYvYkOf7RqWCh4RrXb0LHc2pKvdE9MV0W5+NBHwhqq7eh30L0=
        System.out.println("加密前：" + sourceStr);
        byte[] encryptStrByte = RSAUtils.encryptByPublicKey(sourceStr.getBytes(), publicKey);
        byte[] btt = Base64.encodeBase64(encryptStrByte);
        String encryptStr = new String(btt);
        System.out.println("加密后：" + encryptStr);
        System.out.println("长度：" + encryptStr.length());

        // 私钥解密
        byte[] decryptStrByte = RSAUtils.decryptByPrivateKey(Base64.decodeBase64(encryptStr), privateKey);
        String sourceStr_1 = new String(decryptStrByte);
        System.out.println("解密后：" + sourceStr_1);

    }
}
